package com.flashtones {
	
	import com.quasimondo.geom.ColorMatrix;
	
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.filters.ColorMatrixFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.media.Camera;
	import flash.media.Video;
	
	public class Main extends MovieClip {
	    private var video : Video = new Video();
	    private var camera : Camera = Camera.getCamera();
	    private var squaresContainer : MovieClip = new MovieClip();
	    private var bitmapData : BitmapData = new BitmapData(video.width, video.height);
	    private var oldBitmapData : BitmapData = bitmapData.clone();
	
	    private var soundGen : SoundGenerator = new SoundGenerator();
	
	    private var clips : Array = [];
	    private var squares : Array = [];
	
	    private var differencePoint : Point = new Point(0, 0);
	    private var oldDifferencePoint : Point = new Point(0, 0);
	
	    public var squareWidth : Number = 70;
	    public var differenceThreshold : uint = 50;
	
	    public function Main() : void {
	        super();
	
	        camera.setQuality(0, 0);
	        video.attachCamera(camera);
	
	        squaresContainer.blendMode = BlendMode.ADD;
	        squaresContainer.filters = [ new BlurFilter(20, 20) ];
	
	        //create the squares
	        for (var xx : int = 0;
	             xx * squareWidth < stage.stageWidth + squareWidth * 2;
	             ++xx) {
	            for (var yy : int = 0;
	                 yy * squareWidth < stage.stageHeight + squareWidth * 2;
	                 ++yy) {
	
	                //squares are contained in clips to allow for seperate movement,
	                // filters, and blend modes
	                var clip : MovieClip = new MovieClip();
	                var square : MovieClip = new MovieClip();
	
	                //place the clip
	                clip.x = xx * squareWidth;
	                clip.y = yy * squareWidth;
	
	                //place the square in the center of the clip
	                square.x = -squareWidth / 2;
	                square.y = -squareWidth / 2;
	
	                clip.addChild(square);
	
	                squares.push(square);
	                clips.push(clip);
	
	                var matrix : ColorMatrix = new ColorMatrix();
	
	                //set the brightness to a factor based on the x and y so we
	                // get a diagonal striping
	                var n : Number = Math.sin((Math.PI - clip.x + clip.y) / 10)
	                    * 50 - 100;
	                matrix.adjustBrightness(n);
	
	                //set the hue based on the x value to get vertical color stripes
	                matrix.adjustHue(xx * squareWidth / 2);
	
	                clip.filters = [ new ColorMatrixFilter(matrix.matrix) ];
	        
	                squaresContainer.addChild(clip);
	            }
	        }
	
	        addChild(squaresContainer);
	
	        soundGen.play();
	
	        addEventListener(Event.ENTER_FRAME, onEnterFrame);
	    }
	
	    private function onEnterFrame(e : Event) : void {
	        //grab the video
	        bitmapData.draw(video);
	
	        var differenceBitmapData : BitmapData = new BitmapData(bitmapData.width, bitmapData.height);
	
	        var c1 : uint;
	        var c2 : uint;
	        var redDifference : uint;
	        var greenDifference : uint;
	        var blueDifference : uint;
	        var xSum : uint = 0;
	        var ySum : uint = 0;
	        var numDiff : uint = 0;
	        for (var xb : uint = 0; xb < bitmapData.width; ++xb) {
	            for (var yb : uint = 0; yb < bitmapData.height; ++yb) {
	
	                //grab the old and new colors
	                c1 = oldBitmapData.getPixel(xb, yb);
	                c2 = bitmapData.getPixel(xb, yb);
	
	                //get the absolute difference between the 3 colors individually
	                redDifference = Math.abs((c1 >> 16) - (c2 >> 16));
	                greenDifference = Math.abs(((c1 >> 8) & 0xFF)
	                                           - ((c2 >> 8) & 0xFF));
	                blueDifference = Math.abs((c1 & 0xFF) - (c2 & 0xFF));
				
	                if (redDifference + greenDifference + blueDifference
	                    > differenceThreshold) {
	
	                    xSum += xb;
	                    ySum += yb;
	
	                    ++numDiff;
	
	                    //set the pixel on the difference map
	                    differenceBitmapData.setPixel(xb, yb, 0xFF6666);
	                }
	            }
	        }
	
	        //save the old bitmap
	        oldBitmapData = bitmapData.clone();
	
	        //save the old difference point
	        oldDifferencePoint.x = differencePoint.x;
	        oldDifferencePoint.y = differencePoint.y;
	
	        //calculate the new difference point
	        differencePoint.x = xSum / numDiff;
	        differencePoint.y = ySum / numDiff;
	
	        //set the frequency to 0.005 if no changed pixels are found
	        // otherwise use the x value to calculate a decent frequency.
	        // Dividing by 10 and taking the floor gives us distinct tones
	        soundGen.frequency = Math.floor((isNaN(differencePoint.x)
	                                         ? 0
	                                         : differencePoint.x / 2)
	                                        / 10)
	            * 30;
	
	        //set the volume based on the y value
	        soundGen.volume = 10 - (differencePoint.y + stage.stageHeight / 2) / stage.stageHeight * 10;
	
	        //scale the video to fit the stage
	        var mat : Matrix = new Matrix();
	        mat.scale(stage.stageWidth / video.width,
	                  stage.stageHeight / video.height);
	
	        //clear the graphics and draw the video
	        graphics.clear();
	        graphics.beginBitmapFill(bitmapData, mat, false, true);
	        graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
	        graphics.endFill();
	
	        //loop through the squares
	        for (var i : uint = 0; i < squares.length; ++i) {
	            var clip : MovieClip = clips[i];
	            var square : MovieClip = squares[i];
	
	            //calculate the distance from the difference point
	            var dist : Number
	                = Math.sqrt(Math.pow(clip.x - differencePoint.x * 2, 2)
	                            + Math.pow(clip.y - differencePoint.y * 2, 2));
	
	            //set the depth based on the distance
	            clip.z = dist - 50;
	
	            /*
	            //rotate the square based on "speed" and change of location
	            if (dist != 0
	                && !isNaN(dist)
	                && !isNaN(oldDifferencePoint.x)
	                && !isNaN(differencePoint.x)) {
	
	                square.rotationY += (oldDifferencePoint.x - differencePoint.x)
	                    / dist * 100;
	                square.rotationX += (oldDifferencePoint.y - differencePoint.y)
	                    / dist * 100;
	            }
	            */
				
				square.rotationX = (clip.y - differencePoint.y * 2) / 3;
				square.rotationY = (clip.x - differencePoint.x * 2) / 3;

				//clip.alpha = 0.8 - (clip.z/1200);
	
	            //scale the video to fit the stage
	            mat = new Matrix();
	            mat.scale(stage.stageWidth / video.width,
	                      stage.stageHeight / video.height);
	
	            //translate the bitmap to map the right bit to this square
	            mat.translate(-clip.x + squareWidth / 2,
	                          -clip.y + squareWidth / 2);
	
	            //clear graphics and draw the bitmap
	            square.graphics.clear();
	            square.graphics.beginBitmapFill(differenceBitmapData,
	                                            mat,
	                                            false,
	                                            true);
	            //draw around the center
	            square.graphics.drawRect(-squareWidth / 2,
	                                     -squareWidth / 2,
	                                     squareWidth,
	                                     squareWidth);
	            square.graphics.endFill();
	        }
	    }
	}
}
